﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPFBaseChartCommon.TransitioningContentControl
{
    /// <summary>
    /// 按照步骤 1a 或 1b 操作，然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
    ///
    /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:WPFBaseChartCommon.TransitioningContentControl"
    ///
    ///
    /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:WPFBaseChartCommon.TransitioningContentControl;assembly=WPFBaseChartCommon.TransitioningContentControl"
    ///
    /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用，
    /// 并重新生成以避免编译错误:
    ///
    ///     在解决方案资源管理器中右击目标项目，然后依次单击
    ///     “添加引用”->“项目”->[浏览查找并选择此项目]
    ///
    ///
    /// 步骤 2)
    /// 继续操作并在 XAML 文件中使用控件。
    ///
    ///     <MyNamespace:TransitioningContentControl/>
    ///
    /// </summary>
    public class TransitioningContentControl : ContentControl
    {
        public static readonly DependencyProperty TransitionTypeProperty =
            DependencyProperty.Register("TransitionType", typeof(TransitionType),
                typeof(TransitioningContentControl), new PropertyMetadata(TransitionType.Fade));

        public static readonly DependencyProperty DurationProperty =
            DependencyProperty.Register("Duration", typeof(Duration),
                typeof(TransitioningContentControl), new PropertyMetadata(new Duration(TimeSpan.FromSeconds(0.3))));

        static TransitioningContentControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(TransitioningContentControl),
                new FrameworkPropertyMetadata(typeof(TransitioningContentControl)));
        }

        public TransitionType TransitionType
        {
            get => (TransitionType)GetValue(TransitionTypeProperty);
            set => SetValue(TransitionTypeProperty, value);
        }

        public Duration Duration
        {
            get => (Duration)GetValue(DurationProperty);
            set => SetValue(DurationProperty, value);
        }

        private ContentPresenter _currentContent;
        private ContentPresenter _previousContent;

        public override void OnApplyTemplate()
        {
            _currentContent = GetTemplateChild("CurrentContent") as ContentPresenter;
            _previousContent = GetTemplateChild("PreviousContent") as ContentPresenter;
            base.OnApplyTemplate();
        }

        protected override void OnContentChanged(object oldContent, object newContent)
        {
            if (_currentContent == null || _previousContent == null) return;

            // 记录旧内容并启动退出动画
            _previousContent.Content = oldContent;
            BeginAnimateContent(oldContent, newContent);
            base.OnContentChanged(oldContent, newContent);
        }

        private void BeginAnimateContent(object oldContent, object newContent)
        {
            var exitStoryboard = CreateStoryboard(true);
            var enterStoryboard = CreateStoryboard(false);

            exitStoryboard.Completed += (_, __) =>
            {
                _currentContent.Content = newContent;
                enterStoryboard.Begin();
            };

            exitStoryboard.Begin();
        }

        private Storyboard CreateStoryboard(bool isExiting)
        {
            var storyboard = new Storyboard();
            var transform = new TranslateTransform();

            switch (TransitionType)
            {
                case TransitionType.SlideLeft:
                    transform.X = isExiting ? -ActualWidth : ActualWidth;
                    storyboard.Children.Add(CreateDoubleAnimation(transform, "X", isExiting ? 0 : -ActualWidth));
                    break;
                case TransitionType.SlideRight:
                    transform.X = isExiting ? ActualWidth : -ActualWidth;
                    storyboard.Children.Add(CreateDoubleAnimation(transform, "X", isExiting ? 0 : ActualWidth));
                    break;
                case TransitionType.Fade:
                    storyboard.Children.Add(CreateDoubleAnimation(null, "Opacity", isExiting ? 0 : 1));
                    break;
            }

            Storyboard.SetTarget(storyboard, isExiting ? _previousContent : _currentContent);
            Storyboard.SetTargetProperty(storyboard, new PropertyPath(isExiting ? "RenderTransform.X" : "Opacity"));

            return storyboard;
        }

        private DoubleAnimation CreateDoubleAnimation(TranslateTransform transform, string property, double toValue)
        {
            return new DoubleAnimation
            {
                To = toValue,
                Duration = Duration,
                EasingFunction = new CubicEase { EasingMode = EasingMode.EaseInOut }
            };
        }
    }

    public enum TransitionType
    {
        SlideLeft,
        SlideRight,
        Fade
    }
}
